////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Shader extension
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   CommonZPass.cfi
//  Version:     v1.00
//  Created:     23/11/2004 by Tiago Sousa
//  Compilers:   
//  Description: Common/Shared passes bettwen shaders. Should be included after
//    main shader technique
//
//  NOTE: ALWAYS MAKE SURE VERTEX INPUTS MATCH CORRECTLY BEFORE INCLUDING THIS!
//  TODO: Add support for texture modifiers, since Illum and others require it
//  "Common_" name convention - for shader sharing in cache files
//
//
// -------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
//                Z PASS
////////////////////////////////////////////////////////

sampler2D noiseSampler = sampler_state
{
  Texture = EngineAssets/Textures/noise.dds;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
  MipFilter = LINEAR; 
  AddressU = Wrap;
  AddressV = Wrap;
};

///////////////// vertex input/output //////////////////

struct pixoutZPass
{
  half4 Color : COLOR0;

#if !VS_ALPHABLEND && !PS3 && !XENON
  half4 Color1 : COLOR1;
#endif

#if D3D11
  float Color2 : COLOR2;
#endif

#if VS_ALPHATEST
	#if %_RT_MSAA_QUALITY || %_RT_MSAA_QUALITY1
		uint uCoverageMask	: SV_Coverage;
	#endif
#endif
};

///////////////// vertex shaders //////////////////
vert2fragZ Common_ZPassVS(app2vertZGeneral IN_common)
{
  vert2fragZ OUT = (vert2fragZ)0;

	app2vertZGeneral IN = IN_common;
#if TEMP_TERRAIN
	IN.vertCommon.Position.z = IN_common.vertCommon.baseTC.x;
#endif

  VSVertexContext vertPassPos = (VSVertexContext)0;
  streamPos_FromZ(IN, vertPassPos);

#if %SILHOUETTE_PARALLAX_OCCLUSION_MAPPING
	const float3 SilPomDispl = SilPomDisplacement * TangNormal(IN.vertCommon.Tangent, IN.vertCommon.Binormal);
	vertPassPos.Position.xyz += SilPomDispl * -HeightBias;
#endif

#if TEMP_TERRAIN
  float4 HPosition = Pos_VS_Terrain(0.001f, g_VS_ViewProjZeroMatr, vertPassPos);
#else  
  float4 HPosition = Pos_VS_General(g_VS_ViewProjZeroMatr, vertPassPos);  
#endif

  float3 vPos = vertPassPos.WorldPos.xyz;

#if VS_TESSELLATION && !_RT_NO_TESSELLATION
	//relative possition
	OUT.vView.xyz = vPos.xyz;
	OUT.vNormal.xyz = normalize(mul((const float3x3)vertPassPos.InstMatrix, vertPassPos.Normal.xyz));
#else
	OUT.HPosition = HPosition;
#endif

#if !_RT_OBJ_IDENTITY
  vPos += g_VS_WorldViewPos.xyz;
#endif


  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Output ZInfo

  #ifdef (!PS3 && !XENON) || %_RT_SCATTERSHADE
    OUT.ZInfo.x = HPosition.w * g_VS_NearFarClipDist.w;

    #if %_RT_NEAREST
      OUT.ZInfo.x *= g_VS_NearFarClipDist.z;
    #endif

    #ifdef !PS3 && !XENON
      #if %PARALLAX_OCCLUSION_MAPPING
        OUT.ZInfo.yzw = vPos;
      #endif
    #endif
  #endif


  #if !TEMP_TERRAIN
    // Note: Normalization required for normal diffuse map in world space in case scale used - Do not remove
    float3 worldTangentS = normalize( mul((const float3x3)vertPassPos.InstMatrix, vertPassPos.ObjToTangentSpace[0]) );
    float3 worldTangentT = normalize( mul((const float3x3)vertPassPos.InstMatrix, vertPassPos.ObjToTangentSpace[1]) );
    float3 worldTangentN = cross( worldTangentS, worldTangentT) * vertPassPos.Tangent.w;

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Output tangent space view vector

    #if %OFFSETBUMPMAPPING || %PARALLAX_OCCLUSION_MAPPING
	    OUT.viewTS = mul(float3x3(worldTangentS, worldTangentT, worldTangentN), vPos.xyz - g_VS_WorldViewPos.xyz);
    #else 
      // Support for double sided lighting  - todo: should always do for double sided rendering, not just vegetation
      #if %_RT_VEGETATION        
				#if %LEAVES || %GRASS
          float3 viewVector = normalize(-vertPassPos.WorldPos.xyz );
          float fFlipNormal = min( dot(viewVector, worldTangentN) +0.1,1.f);
          vertPassPos.Tangent.w *= fFlipNormal;
          worldTangentN *= fFlipNormal;
				#endif
      #endif
    #endif

    float4 baseTC = vertPassPos.baseTC;  

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Output uv coordinates

    _TCModify(baseTC, OUT.baseTC, vertPassPos.Position, vertPassPos.ObjToTangentSpace[2], TS_DIFFUSE);

    #if VS_ALPHATEST || _RT_DISSOLVE
			float4 fAlphaTest = 0.0f;
      #if _RT_DISSOLVE
        // For dissolve and instancing, we use PI_AlphaTest
        fAlphaTest = AlphaTest;
        #if _RT_INSTANCING_ATTR
          {
            float4 baseTC;
            #fetchinst (baseTC = IN.InstAlphaTest, vertPassPos.nInstance);
            fAlphaTest = baseTC;
          }
        #endif
      #else
        // For regular materials using alpha test, we use PB_AlphaTest
        fAlphaTest = PBAlphaTest;
      #endif
      OUT.baseTC.wz = fAlphaTest.wx;
      #if %LEAVES
				OUT.baseTC.w = ParallelOpacityFalloff(vertPassPos.WorldPos.xyz, worldTangentN.xyz, CapOpacityFalloff, fAlphaTest.w);
      #endif
    #endif

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Output tangents

	  #if %GRASS
		  OUT.vNormal.xyz = worldTangentN*0.5+0.5;
		  #if _RT_DISSOLVE
				OUT.vNormal.w = fAlphaTest.y;
		  #endif
	  #else
		  OUT.vTangent = float4(worldTangentS, vertPassPos.Tangent.w); 
		  OUT.vBinormal.xyz = worldTangentT;
		  #if _RT_DISSOLVE
				OUT.vBinormal.w = fAlphaTest.y;
		  #endif
	  #endif
		
		////////////////////////////////////////////////////////////////////////////////////////////////
		// Output color
		
		#if %BLENDLAYER
			OUT.Color = IN.vertCommon.Color;
		#endif
		
  #else

    // Output terrain related data. Terrain is separated into 2 diferent passes (base and detail layers)

    #if !_RT_OBJ_IDENTITY
      vertPassPos.WorldPos.xyz += g_VS_WorldViewPos.xyz;
    #endif

    // Detail layers
    #if VS_ALPHABLEND
      float4 tcLayer;

      float3 vBumpPos = vertPassPos.WorldPos.xyz;
      vBumpPos.xy -= LayerTexGen[3].zw;

#if CAFE
      tcLayer.xy = mul((float3x3)LayerTexGen, vBumpPos).xy;
#else
      tcLayer.xy = mul((float2x3)LayerTexGen, vBumpPos);
#endif
      tcLayer.z = 0;
      tcLayer.w = vertPassPos.Position.w;

      float4 bumpTC;
      _TCModify(tcLayer, bumpTC, vertPassPos.Position, vertPassPos.Normal, TS_BUMP);
      OUT.baseTC.xy = bumpTC.xy;

// TODO: unify terrain detail mapping
//      float3 vDetailPos = vertPassPos.WorldPos.xyz;
//      vDetailPos.xy -= LayerTexGen_DetUV_ZSlope.xy;
//      tcLayer.xy = mul((float2x3)LayerTexGen, vDetailPos);
      // HACK: DetailParams.xy - is a dettex tiling. we need to apply this directly since we have no full suuport for dettex in _TCModify: _TCGMatrixDecal???
//      OUT.baseTC.zw = tcLayer.xy * DetailParams.xy;

      // get tangent space info from texgen parameters
      float3 Tangent = normalize(LayerTexGen[0].xyz);
      float3 Binormal = normalize(LayerTexGen[1].xyz);

      // project tangent and binormal to plane perpendicular to the normal
      Tangent-=dot(Tangent,vertPassPos.Normal)*vertPassPos.Normal;
      Binormal-=dot(Binormal,vertPassPos.Normal)*vertPassPos.Normal;
      Tangent=normalize(Tangent);
      Binormal=normalize(Binormal);

      float4 Color = vertPassPos.Color;           
      if (Color.g > 127.f/255.f)
        Color.g -= 128.f/255.f;

      float fWeight = saturate(1 - abs(Color.g*255 - LayerTexGen[2].w));	// saturate needed for dx10 HDR
      float3 NormalAbs = abs(vertPassPos.Normal.xyz); 

			if( NormalAbs.z > LayerTexGen_DetUV_ZSlope.z ) fWeight *= LayerTexGen[2].z;
			else
			{
				fWeight *= NormalAbs.x > NormalAbs.y ? LayerTexGen[2].x : LayerTexGen[2].y;
			}

      float fDist = length(vertPassPos.WorldPos.xyz - g_VS_WorldViewPos.xyz);
      fDist = pow(min(fDist/max(0.001, LayerTexGen[0].w), 1), 4);
      OUT.vTangent.w =  fWeight * (1-fDist);
      OUT.vTangent.xyz = Tangent.xyz;    
      OUT.vBinormal.xyz = Binormal;
      OUT.vNormal.xyz = vertPassPos.Normal;

			#if %OFFSETBUMPMAPPING || %PARALLAX_OCCLUSION_MAPPING 
				OUT.viewTS = mul(float3x3(OUT.vTangent.xyz, OUT.vBinormal.xyz, OUT.vNormal.xyz), vPos.xyz - g_VS_WorldViewPos.xyz);
			#endif
    #else
			OUT.baseTC = vertPassPos.baseTC;
      OUT.vNormal.xyz = vertPassPos.Normal*0.5+0.5;
    #endif

  #endif
  
  return OUT;            
}

///////////////// pixel shaders //////////////////

// Setup alpha coverage/alpha value
void GetAlphaMS( in float2 baseTC, in half fAlphaRef, inout half fDiffuseAlpha, inout pixoutZPass OUT) 
{
#if VS_ALPHATEST

#if %_RT_MSAA_QUALITY || %_RT_MSAA_QUALITY1
	fDiffuseAlpha = 0;

	float2 vDDX = ddx( baseTC.xy );
	float2 vDDY = ddy( baseTC.xy );

	int nSamplesNum = GetMSAASampleNum();

	// DX11 has standardized MSAA offsets
#if %_RT_MSAA_QUALITY && %_RT_MSAA_QUALITY1
	static const float2 vMSAAOffsets[8] = 
	{ 
		float2(0.0625, -0.1875),  float2(-0.0625,  0.1875),
		float2(0.3125,  0.0625),  float2(-0.1875, -0.3125),
		float2(-0.3125,  0.3125), float2(-0.4375, -0.0625),
		float2(0.1875,  0.4375),  float2(0.4375, -0.4375)
	};
#elif %_RT_MSAA_QUALITY && !%_RT_MSAA_QUALITY1
	static const float2 vMSAAOffsets[4] = 
	{ 
		float2(-0.125, -0.375), float2(0.375, -0.125),
		float2(-0.375,  0.125), float2(0.125,  0.375)
	};
#elif !%_RT_MSAA_QUALITY && %_RT_MSAA_QUALITY1
	static const float2 vMSAAOffsets[2] = 
	{ 
		float2(0.25, 0.25), float2(-0.25, -0.25) 
	};
#endif

	// Loop through all sub-samples
	float2 vDifAlphaAcc = float2(0, 0);
	[unroll] for( int i = 0; i < nSamplesNum; ++i )
	{
		float2 v2TexelOffset = vMSAAOffsets[i].x * vDDX + vMSAAOffsets[i].y * vDDY;
		float fSampleDiffuseAlpha = tex2D( diffuseMapSampler, baseTC + v2TexelOffset).w;

		if( ( fSampleDiffuseAlpha - fAlphaRef ) >= 0 )
		{ 
			// Update output coverage mask 
			OUT.uCoverageMask |= ( uint(0x1) << i );
			vDifAlphaAcc.xy += float2(fSampleDiffuseAlpha, 1);
		}
	}

	// Output average of summed alpha
	if( vDifAlphaAcc.y )
		fDiffuseAlpha = vDifAlphaAcc.x / vDifAlphaAcc.y;
#endif

#endif
}

///////////////// pixel shaders //////////////////

pixoutZPass Common_ZPassPS(vert2fragZ IN, IN_WPOS)
{
#if PS3  
  #pragma sce-cgc("-texformat default COMPRESSED_RGBA_S3TC_DXT5");
  #pragma sce-cgc("-fastprecision");  
#endif

	pixoutZPass OUT = (pixoutZPass) 1;

  ////////////////////////////////////////////////////////////////////////////////////////////////
  // Depth and alpha test processing

#if _RT_DISSOLVE && !TEMP_TERRAIN
  #if !%GRASS
	  float DissolveMul = IN.vBinormal.w;
  #else
  	float DissolveMul = IN.vNormal.w;
  #endif
#endif

  float4 ZInfo = 0;
  #ifdef (!PS3 && !XENON) || %_RT_SCATTERSHADE
    ZInfo = IN.ZInfo;
  #endif

  float fZ = ZInfo.x;

	half blendFac = 0;
	float blendLayer2Tiling = 0;
#if %BLENDLAYER
#ifdef CAFE
	blendFac = GetLayerBlendingValue(BlendMapSampler, IN.baseTC.xy, 0, IN.Color.a, BlendFactor, BlendFalloff, false);
#else	
	blendFac = GetLayerBlendingValue(BlendMapSampler, IN.baseTC.xy, 0, IN.Color.a, BlendFactor, BlendFalloff);	
#endif
	blendLayer2Tiling = BlendLayer2Tiling;
#endif

  int mdQuality;
  half mdDisplacement;
  half mdHeightBias;
  half mdSelfShadowStrength;
  GetMicroDetailParams(mdQuality, mdDisplacement, mdHeightBias, mdSelfShadowStrength);

  if (mdQuality == MICRO_DETAIL_QUALITY_OBM)
  {
#if %OFFSETBUMPMAPPING // still needed to compile as we refer to IN.viewTS
  #if !TEMP_TERRAIN || VS_ALPHABLEND
    #if PS3
    IN.baseTC.xy = OffsetMap(IN.baseTC.xy, normalize(-IN.viewTS), 1, mdDisplacement, mdHeightBias, blendFac, blendLayer2Tiling);
    #else
    IN.baseTC.xy = OffsetMap(IN.baseTC.xy, normalize(-IN.viewTS), 2, mdDisplacement, mdHeightBias, blendFac, blendLayer2Tiling);
    #endif
  #endif
#endif
#ifdef CAFE
	float cafeDummy = 0;
#endif
  }
  else if(mdQuality == MICRO_DETAIL_QUALITY_POM)
  {
#if %PARALLAX_OCCLUSION_MAPPING // still needed to compile as we refer to IN.viewTS
  #if !%TEMP_VEGETATION && (!TEMP_TERRAIN || VS_ALPHABLEND)
    const float lod = ComputeTextureLOD(IN.baseTC.xy, ResInfoBump.xy);

    const float2 offsetBest = ParallaxOcclusionMap(IN.baseTC.xy, lod, normalize(IN.viewTS), 15, mdDisplacement, mdHeightBias, blendFac, blendLayer2Tiling);
    #if !TEMP_TERRAIN
    {
      float2 delta = offsetBest - IN.baseTC.xy;
      float sgnDelta = dot(delta, IN.viewTS.xy) >= 0 ? 4 : -4;
      fZ += sgnDelta * saturate(length(delta)) * PS_NearFarClipDist.w;
    }
    #endif
    IN.baseTC.xy = offsetBest;
  #endif
#endif
#ifdef CAFE
	float cafeDummy = 0;
#endif
  }

  #if TEMP_TERRAIN

    OUT.Color = EncodeSceneDepthNoAlpha(fZ, 0, ZInfo.yzw);

  #else

	  #if VS_ALPHATEST

      half fDiffuseAlpha = tex2D(diffuseMapSampler, IN.baseTC.xy).w;
			GetAlphaMS( IN.baseTC.xy, IN.baseTC.w, fDiffuseAlpha, OUT);
      OUT.Color = EncodeSceneDepthWithAlpha(fZ, fDiffuseAlpha, IN.baseTC.w, ZInfo.yzw);

	  #else

      OUT.Color = EncodeSceneDepthNoAlpha(fZ, 1, ZInfo.yzw);

	  #endif

    #if _RT_DISSOLVE && !TEMP_TERRAIN
    	half2 noiseTC = WPos.xy/100.0f;
    	
		  half fNoise = tex2D(noiseSampler, noiseTC).g;

  		clip(DissolveMul*(fNoise-IN.baseTC.z));
    #endif

  #endif

  ////////////////////////////////////////////////////////////////////////////////////////////////
  // Normals processing

  half4 cOutNormal = 0;
  half3 vNormalTS = half3(0,0,1);

  #if !TEMP_TERRAIN && !%GRASS
	  half3x3 mTangentToWS = half3x3(IN.vTangent.xyz, IN.vBinormal.xyz, cross(IN.vTangent.xyz, IN.vBinormal.xyz)*IN.vTangent.w);  
    #if %_RT_SHADER_LOD
      vNormalTS = mTangentToWS[2];
    #else
	    vNormalTS = GetNormalMap(bumpMapSampler, IN.baseTC.xy);
    #endif
  #endif

  #if TEMP_TERRAIN  
	  #if VS_ALPHABLEND
		  // terrain detail layers
		  half3x3 mTangentToWS = half3x3( IN.vTangent.xyz, IN.vBinormal.xyz, IN.vNormal.xyz);  
      #if %_RT_SHADER_LOD
        vNormalTS = mTangentToWS[2];
      #else
		    vNormalTS = GetNormalMap(bumpMapSampler, IN.baseTC.xy);
      #endif

	  #else
		  // terrain base layer
		  half3 vNormal = IN.vNormal.xyz; 			
	  #endif
  #endif


	#if %BLENDLAYER
		half3 vNormal2 = GetNormalMap( BumpMap2Sampler, IN.baseTC.xy * blendLayer2Tiling );
		vNormalTS = lerp( vNormalTS, vNormal2, blendFac );
	#endif

  #if %DETAIL_TEXTURE_IS_SET && !%_RT_SHADER_LOD
	  #if %DETAIL_BUMP_MAPPING
    
		  float2 vDetailBumpTilling = DetailBumpTillingU*DetailParams.xy;
		  float fDetailBumpScale = DetailBumpScale;

			// TODO: unify terrain detail mapping
		  //#if !TEMP_TERRAIN || !VS_ALPHABLEND // not a terrain layer
		  float2 tcDetail =  IN.baseTC.xy * vDetailBumpTilling;                                     // 1 inst
		  //#else
		  //float2 tcDetail =  IN.baseTC.zw; // terrain texcoords are cam position dependent - so we need to handle detail tex coord in a special way (see vs)
		  //#endif

			#if %DETAIL_TEXTURE_IS_NORMALMAP
				half2 vDetailN = GetNormalMap(detailMapSampler, tcDetail).xy;
			#else
				half2 vDetailN = EXPAND(tex2D(detailMapSampler, tcDetail).xy);
			#endif

      #if %ALPHAMASK_DETAILMAP
        fDetailBumpScale *= tex2D(diffuseMapSampler, IN.baseTC.xy).w;
      #endif

		  // add detail bump
		  vNormalTS.xy += vDetailN.xy * fDetailBumpScale;			                                          // 1 inst
	  #endif
  #endif

  #if %TEMP_VEGETATION && %GRASS
		  // Specific grass case - no normal lookup
		  half3 vNormal = IN.vNormal.xyz;
  #else
	  #if !TEMP_TERRAIN || VS_ALPHABLEND
      #if %_RT_SHADER_LOD
        float3 vNormal = vNormalTS;
      #else
        half3 vNormal = mul(vNormalTS, mTangentToWS);
		  #endif
      vNormal = vNormal * 0.5h + 0.5h;
    #endif  

  #endif

  #if TEMP_TERRAIN
    // specific power factor just for terrain base
    cOutNormal.xyzw = half4(vNormal.xyz, 16.0h / 255.0h);      
  #else
    cOutNormal.xyzw = half4(vNormal.xyz, MatSpecColor.w);      
  #endif

  #if VS_ALPHABLEND      
    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Alpha blend cases: decals, terrain layers

    #if TEMP_TERRAIN
			cOutNormal.w = ( saturate( IN.vTangent.w ) );

			// Compute non-linear alpha blend factor in case height map present (limited to POM/OM cases)
//			if( mdQuality )
//				cOutNormal.w = GetLayerBlendingValue(bumpHeightMapSampler, IN.baseTC.xy, 0, cOutNormal.w, BlendFactor, BlendFalloff, true);	      
    #else
      half fAlpha= tex2D(diffuseMapSampler, IN.baseTC.xy).w;
      cOutNormal.w = saturate( fAlpha ) ;
    #endif
  #endif

  // on consoles we always use alpha test 
  #if PS3 || XENON
    #if !TEMP_TERRAIN
      #if VS_ALPHATEST
		    #if !%GRASS
			    cOutNormal.a *= half(fDiffuseAlpha > IN.baseTC.w);
		    #else
			    cOutNormal.a = fDiffuseAlpha - IN.baseTC.w;
		    #endif
	    #endif
    #endif
  #endif

  #if %GLOSS_MAP && %SPECULARPOW_GLOSSALPHA
    cOutNormal.w *= GetTexture2D(glossMapSampler, IN.baseTC.xy).w;
  #endif
  
  EncodeDynObjectMask(cOutNormal.w);
  CompressUnsignedNormalToNormalsBuffer(cOutNormal);

  #ifdef VS_ALPHABLEND || PS3 || XENON
    OUT.Color = cOutNormal;
  #else
    OUT.Color1 = cOutNormal;
  #endif

	#ifdef D3D11
		//IN.baseTC.xy = float2(7.5, -7.5);
		OUT.Color2 = EncodeSceneUV(IN.baseTC.xy);
	#endif

	return OUT;
}

#include "CommonZPassTess.cfi"

//////////////////////////////// technique ////////////////

technique ZPass
{
	pass p0
	{
		VertexShader = Common_ZPassVS() ZVS;
#ifdef VS_TESSELLATION
		HullShader   = Common_ZPassHS() GeneralHS;
		DomainShader = Common_ZPassDS() GeneralDS;
#endif
		PixelShader = Common_ZPassPS() ZPS;

		ZEnable = true;
		ZWriteEnable = true;        
		CullMode = Back;        
     
#if %GRASS || %LEAVES || %HAIR_PASS
		CullMode = None;
#endif
	}
}
